﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;

namespace VaccineEbook.Common
{
    public class EncryptionUtil
    {

        private EncryptionUtil() { }
        /// <summary>
        /// Hash a string with the algorithm
        /// </summary>
        /// <param name="input"></param>
        /// <param name="algorithm">MD5, SHA1, SHA256,...</param>
        /// <returns>a base64 string had hash</returns>
        public static string Hash(string input, System.Security.Cryptography.HashAlgorithm algorithm)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            algorithm.Initialize();
            byte[] buffer = algorithm.ComputeHash(encoding.GetBytes(input));
            StringBuilder builder = new StringBuilder();
            foreach (byte num in buffer)
            {
                builder.Append(num.ToString("x2"));
            }
            return builder.ToString();
        }

        /// <summary>
        /// Hash a string with the SHA1 algorithm
        /// </summary>
        /// <param name="input"></param>
        /// <returns>a base64 string had hash</returns>
        public static string HashSHA1(string input)
        {
            return Hash(input, new System.Security.Cryptography.SHA1Managed());
        }

        /// <summary>
        /// Hash a string with the SHA256 algorithm
        /// </summary>
        /// <param name="input"></param>
        /// <returns>a base64 string had hash</returns>
        public static string HashSHA256(string input)
        {
            return Hash(input, new System.Security.Cryptography.SHA256Managed());
        }

        /// <summary>
        /// Encode a string
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static String Encode(String data)
        {
            if (data == null)
                return null;
            return (GetString(Encode(GetBinaryBytes(data))));
        }

        /// <summary>
        /// Encode a string
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static String Encode(String data, String hashKey)
        {
            if (data == null)
                return null;
            if (hashKey == null || hashKey.Length == 0)
                return Encode(data);
            else
            {
                String str = Encode(hashKey);
                String str2 = Encode(data);

                return Encode(str + str2);
            }
        }

        public static String Decode(String data, String hashKey)
        {
            if (data == null)
                return null;
            if (hashKey == null || hashKey.Length == 0)
                return Decode(data);
            else
            {
                String str = Decode(data);
                String str2 = Encode(hashKey);
                if (str2.Length > str.Length)
                    return str + str2;
                else
                {
                    String str3 = str.Substring(str2.Length);
                    String str4 = Decode(str3);
                    return str4;
                }
            }
        }
        /// <summary>
        /// Encode data function
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] Encode(byte[] data)
        {
            int c;
            int len = data.Length;
            StringBuilder ret = new StringBuilder(((len / 3) + 1) * 4);
            for (int i = 0; i < len; ++i)
            {
                c = (data[i] >> 2) & 0x3f;
                ret.Append(cvt[c]);
                c = (data[i] << 4) & 0x3f;
                if (++i < len)
                    c |= (data[i] >> 4) & 0x0f;

                ret.Append(cvt[c]);
                if (i < len)
                {
                    c = (data[i] << 2) & 0x3f;
                    if (++i < len)
                        c |= (data[i] >> 6) & 0x03;

                    ret.Append(cvt[c]);
                }
                else
                {
                    ++i;
                    ret.Append((char)fillchar);
                }

                if (i < len)
                {
                    c = data[i] & 0x3f;
                    ret.Append(cvt[c]);
                }
                else
                {
                    ret.Append((char)fillchar);
                }
            }

            return (GetBinaryBytes(ret.ToString()));
        }

        /// <summary>
        /// Decode a string
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static String Decode(String data)
        {
            if (data == null)
                return null;

            return (GetString(Decode(GetBinaryBytes(data))));
        }

        public static byte[] Decode(byte[] data)
        {
            int c;
            int c1;
            int len = data.Length;
            StringBuilder ret = new StringBuilder((len * 3) / 4);
            for (int i = 0; i < len; ++i)
            {
                c = cvt.IndexOf((char)data[i]);
                ++i;
                c1 = cvt.IndexOf((char)data[i]);
                c = ((c << 2) | ((c1 >> 4) & 0x3));
                ret.Append((char)c);
                if (++i < len)
                {
                    c = data[i];
                    if (fillchar == c)
                        break;

                    c = cvt.IndexOf((char)c);
                    c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
                    ret.Append((char)c1);
                }

                if (++i < len)
                {
                    c1 = data[i];
                    if (fillchar == c1)
                        break;

                    c1 = cvt.IndexOf((char)c1);
                    c = ((c << 6) & 0xc0) | c1;
                    ret.Append((char)c);
                }
            }

            return (GetBinaryBytes(ret.ToString()));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        private static string GetString(byte[] arr)
        {
            StringBuilder buf = new StringBuilder();
            for (int i = 0; i < arr.Length; ++i)
                buf.Append((char)arr[i]);

            return (buf.ToString());
        }

        /// <summary>
        /// Get binary byte function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static byte[] GetBinaryBytes(String str)
        {
            byte[] b = new byte[str.Length];
            for (int i = 0; i < b.Length; ++i)
                b[i] = (byte)str[i];

            return (b);
        }

        private static int fillchar = (int)'=';

        private static string cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        + "abcdefghijklmnopqrstuvwxyz" + "090848208+/";

        protected static String _DefaultHashKey = String.Empty;

        public static String DefaultHashKey
        {
            get { return _DefaultHashKey; }
            set { _DefaultHashKey = value; }
        }

        public static String Encrypt(String str)
        {
            return HashSHA1(str);

        }

        public static String Encrypt(String str, String hashKey)
        {
            return HashSHA1(string.Concat(str, hashKey));
        }

        public static String Decrypt(String str)
        {
            return str;
        }

        public static String Decrypt(String str, String hashKey)
        {
            return str;
        }
        public static String BuildHashKey(int userID, DateTime timestamp)
        {
            long tick = timestamp.Ticks;

            long l2 = (long)userID;
            long l3 = tick * 26 / 88 + l2 * 7 / 4;
            String str = l3.ToString();
            if (str.Length > 10)
                str = str.Substring(3);
            return str;
        }

    }
}